import typing
import ipywidgets as widgets
import interface.utils as Iutils
from ipyvuetify.extra import FileInput

debug = widgets.Output(
    layout={'border': '1px solid black'})

widgets.Style()

class Widgets:
    def __init__(self, interface) -> None:
        """
        interface: interface.Interface
        """
        self.interface = interface

        self.model_label = widgets.HTML(
            value="Нейросеть для обучения: Не выбрана")

        self.models_output = widgets.Output()

        self.save_model_button = widgets.HTML(
            value="""
                <i class="fa fa-floppy-o" style="margin-left: 40px; margin-right: var(--jp-widgets-inline-margin);"></i>
                <button disabled = "true" style="background: none;color: inherit;border: none;padding: 0;font: inherit;cursor: pointer;outline: inherit;">
                    Модель еще не обучена
                </button>""",
            layout={'width': '266px', 'height': '30px'})\
            .add_class('rounded_button_green')

        self.new_model_input = widgets.Text(
            placeholder='Введите имя новой модели',
            disabled=False,
            layout={'width': '302px'}
        )

        self.add_button = widgets.Button(
            description="Создать",
            # icon="plus",
            layout={
                'width': '124px',
                'background-color': '#27AE60'
            }
        ).add_class('model_buttons')
        self.add_button.on_click(self.interface.add_model)

        self.add_model_container = widgets.HBox(
            [self.new_model_input, self.add_button]
        )

        self.uploader = widgets.FileUpload(
            accept='.zip',
            multiple=False,
            description="Выберите архив с датасетом",
            layout=widgets.Layout(width="300px")).add_class('rounded_button')

        self.uploaded_filenames = widgets.HTML(value="")

        self.modelname_input = widgets.Text(
            placeholder='Введите название нейросети')

        self.name_input = widgets.Dropdown(
            options=['image_size', 'batch_size', 'num_epochs'],
            value='image_size',
            description='Введите:',
            disabled=True,
        )

        self.params_display = widgets.VBox(
            self.generate_params(self.interface.params).children)
        self.params_list = widgets.VBox(
            self.generate_params(self.interface.params, True).children)

        self.progress_bar = widgets.FloatProgress(
            value=0, min=0, max=1, description='Прогресс ', layout={
                'width': '633px', 'height': '30px', 'margin-bottom': '5px'})

        self.status = widgets.HTML(
            value="Статус: Ожидание начала обучения")

        self.accuracy_label = widgets.HTML(
            value="Качество обучения:  <b>N/A</b>")

        self.category_label = widgets.HTML(value="Полученный тип документа: ")

        self.warning_label = widgets.HTML(
            value="Внимание! При загрузке нового датасета, старый будет перезаписан")\
            .add_class('warning_label')

        self.train_time_label = widgets.HTML(
            value="Старт / финиш / общее время")

        self.train_time = widgets.HTML(
            value="00:00:00 / 00:00:00 / 0 мин. 0 сек.")

        self.train_button = widgets.Button(
            description="Начать обучение нейросети",
            icon='play',
            layout=widgets.Layout(
                width='231px',
                height='30px')).add_class('rounded_button')
        self.train_button.on_click(self.interface.start_training)

        self.stop_button = widgets.Button(
            description="Остановить", icon='stop', disabled=True,
            layout=widgets.Layout(
                width='128px', height='30px')).add_class('rounded_button')

        self.stop_button.on_click(self.interface.stop_training)

        self.testing_button = widgets.Button(
            description="Начать тестирование", icon='play', disabled=True,
            layout=widgets.Layout(
                width='286px', height='30px')
        ).add_class('rounded_button')
        self.testing_button.on_click(self.interface.start_testing)

        self.test_img_preview = self.image()

        self.fi = FileInput(accept='.zip', multiple=False, show_progress=True)
        self.imagei = FileInput(accept='.jpg, .jpeg, .png, .webp',
                                multiple=False, show_progress=False)
        self.imagei.layout.width = "286px"

        self.fi.observe(self.interface.on_upload_change, names='file_info')
        self.imagei.observe(self.interface.on_test_upload, names='file_info')

        self.datasets_tab = widgets.VBox(
            [
                self.fi,
                self.warning_label,
                self.uploaded_filenames
            ])

        self.parameters_tab = widgets.VBox([
            widgets.HTML(value="<b>Параметры</b>"),
            self.params_display
        ])

        self.training_info = widgets.VBox([
            self.status,
            self.accuracy_label,
            self.train_time_label,
            self.train_time])\
            .add_class('status_container')

        self.params_container = widgets.VBox(
            [self.params_list])\
            .add_class('params_container')

        self.training_tab_left = widgets.VBox(
            [
                widgets.HTML(value="<b>Обучение</b>"),
                widgets.HTML(value="Список параметров")
                .add_class('list_label'),
                widgets.HTML(
                    value='<span class="nm"><b>Наименование</b></span><style>.nm {margin-right: 190px} </style> <b>Значение</b>')
                .add_class('list_name'),
                self.params_container
            ]
        )\
            .add_class('training_container')

        self.training_tab_bottom = widgets.VBox([
            self.progress_bar,
            widgets.HBox(
                [
                    self.train_button,
                    self.stop_button,
                    self.save_model_button
                ])
        ])

        self.training_containter = widgets.HBox(
            [
                self.training_tab_left,
                self.training_info
            ]
        )

        self.training_tab = widgets.VBox(
            [
                self.training_containter,
                self.training_tab_bottom
            ])

        self.testing_tab = widgets.VBox(
            [
                widgets.HTML(value="<b>Тестирование</b>"),
                widgets.HBox(
                    [
                        widgets.VBox(
                            [
                                self.imagei,
                                self.testing_button,
                                self.category_label
                            ]), self.test_img_preview])
            ])

        self.tab = widgets.Tab().add_class('main_tab')

        self.models_tab = widgets.VBox([
            widgets.HTML(value="<b>Выбор нейросети для обучения</b>"),
            self.model_label,
            self.add_model_container,
            self.models_output
        ])
        self.tab.children = [
            self.models_tab,
            self.datasets_tab,
            self.parameters_tab,
            self.training_tab,
            self.testing_tab
        ]
        self.tab.set_title(0, 'Модели')
        self.tab.set_title(1, 'Датасеты')
        self.tab.set_title(2, 'Параметры')
        self.tab.set_title(3, 'Обучение')
        self.tab.set_title(4, 'Тестирование')

        self.prev_button = widgets.Button(
            layout={'width': '125px', 'height': '32px'},
            icon='fa-arrow-circle-left').add_class('rounded_button')
        self.prev_button.on_click(self.prev_button_callback)

        self.next_button = widgets.Button(
            layout={'width': '125px', 'height': '32px'},
            icon='fa-arrow-circle-right').add_class('rounded_button')
        self.next_button.on_click(self.next_button_callback)

        self.nav_buttons = widgets.HBox(
            [self.prev_button, self.next_button]
        )\
            .add_class('nav_buttons_box')
        self.notify_output = widgets.Output()

    def build_style(self, raw_style):
        return widgets.HTML(f"""<style>{raw_style}</style>""")

    def update_display(self, to_edit):
        self.params_display.children = self.generate_params(
            params=self.interface.params,
            only_list=False,
            to_edit=to_edit).children
        self.params_list.children = self.generate_params(
            params=self.interface.params,
            only_list=True).children

    @staticmethod
    def edit_label(name: str, value: typing.Any, disabled_status: bool) -> typing.Tuple[widgets.Text]:
        return (
            widgets.Text(value=name, disabled=True),
            widgets.Text(
                value=value,
                disabled=disabled_status, layout={'width': '148px'})
        )

    @staticmethod
    def edit_button(disabled_status: bool | None, index: int, value: str) -> widgets.Button:
        _edit_button = widgets.Button(
            layout=widgets.Layout(
                width='30px', height='28px')) \
            .add_class('edit_button')
        if disabled_status is False:
            _edit_button.icon = "floppy-o"
            _edit_button.style.button_color = '#27AE60'
            _edit_button.on_click(
                lambda self, call, index=index, value=value: self.interface.save_param(index, value))
        else:
            _edit_button.icon = "far fa-edit"
            _edit_button.style.button_color = '#163D6C'
            _edit_button.on_click(
                lambda self, call, index=index: self.interface.edit_param(index))
        return _edit_button

    def generate_params(self, params, only_list=False, to_edit=[]):
        rows = []
        for index, param in enumerate(params):
            disabled_status = False if param['name'] in to_edit else True
            name, value = self.edit_label(
                param["name"], param["value"], disabled_status
            )
            _extra = [name, value]
            if not only_list:
                _extra.append(self.edit_button(disabled_status, index, value))
            rows.append(widgets.HBox([name, value]))
        return widgets.VBox(rows)

    def generate_models(self, folders):
        model_rows = []
        for model in folders:
            select_button = widgets.Button(
                layout={'width': '60px'}, icon='check').add_class('model_buttons')
            if model != self.interface.selected_model:
                select_button.style.button_color = '#163D6C'
            else:
                select_button.style.button_color = 'green'
            select_button.on_click(
                lambda call, model_name=model: self.interface.select_model(model_name))
            delete_button = widgets.Button(
                layout={'width': '60px'}, icon='trash').add_class('model_buttons')
            delete_button.style.button_color = '#E22C38'
            delete_button.on_click(
                lambda call, model_name=model: self.interface.delete_model(model_name))
            model_rows.append(widgets.HBox(
                [widgets.Text(value=model, disabled=True),
                 select_button, delete_button]))
        return widgets.VBox(model_rows)

    @staticmethod
    def image():
        return widgets.Image(
            value=Iutils.read_file(
                "interface/source/placeholder.jpg", "rb"),
            format='jpg',
            width=127,
            height=171,
            layout=widgets.Layout(margin="0px 0px 0px 50%")
        )

    def prev_button_callback(self, call):
        self.tab.selected_index -= 1 if self.tab.selected_index != 0 else None

    def next_button_callback(self, call):
        self.tab.selected_index += 1 if self.tab.selected_index != 4 else None

    def tab_observe(self):
        self.tab.observe(
            self.interface.on_update_selected_index,
            names='selected_index')
